﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace XCrawler
{
    public static class StringExtensions
    {
        #region 字符串分割
        /// <summary>
        /// 根据标志分割字符串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="sign">分割标识</param>
        /// <param name="option">分割结果去重方式</param>
        /// <returns></returns>

        public static string[] SplitBySign(this string str, string sign, StringSplitOptions option = StringSplitOptions.None) => str?.Split(new string[] { sign }, option) ?? new string[0];
        /// <summary>
        /// 根据标志分割字符串(不包含空字符串)
        /// </summary>
        /// <param name="str"></param>
        /// <param name="sign"></param>
        /// <returns></returns>
        public static string[] SplitBySignWithoutEmpty(this string str, string sign) => str.SplitBySign(sign, StringSplitOptions.RemoveEmptyEntries);

        /// <summary>
        /// 根据逗号分割字符串(不包含空字符串)
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string[] SplitByComma(this string str) => str.SplitBySignWithoutEmpty(",");


        /// <summary>
        /// 根据标志分割字符串后获得第一个字符串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="sign"></param>
        /// <param name="option"></param>
        /// <returns></returns>
        public static string SplitThenGetFirst(this string str, string sign, StringSplitOptions option = StringSplitOptions.RemoveEmptyEntries) => str?.SplitBySign(sign, option).FirstOrDefault();
        /// <summary>
        /// 根据标志分割字符串后获得最后一个字符串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="sign">分割标志</param>
        /// <param name="option">分割结果去重方式</param>
        /// <returns></returns>
        public static string SplitThenGetLast(this string str, string sign, StringSplitOptions option = StringSplitOptions.RemoveEmptyEntries) => str?.SplitBySign(sign, option).LastOrDefault();

        /// <summary>
        /// 根据标志分割字符串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="signs">分割标识，多重标识</param>
        /// <returns></returns>
        public static string[] SplitByMultiSign(this string str, params string[] signs) => str?.Split(signs, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];
        /// <summary>
        /// 根据 | \ / 、 ， , 空格 中文空格 制表符空格换行 分割字符串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="option">分割结果去重方式</param>
        /// <returns></returns>
        public static string[] SplitAuto(this string str, StringSplitOptions option = StringSplitOptions.RemoveEmptyEntries) =>
            str.SplitByMultiSign("|", "\\", "/", "、", ":", "：", "，", ",", "　", " ", "\t");
        /// <summary>
        /// 根据换行符拆分字符串
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string[] SplitByLine(this string str) => str.SplitByMultiSign("\r\n", "\r", "\n");

        /// <summary>
        /// 根据竖线符 | 拆分字符串
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string[] SplitByYLine(this string str) => str.SplitByMultiSign("|");

        /// <summary>
        /// 根据逗号拆分字符串并转换成int
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static int[] SplitToInts(this string str) => Array.ConvertAll<string, int>(str.SplitByMultiSign(","), (s) => { return int.Parse(s); });


        /// <summary>
        /// 列表转换成逗号相隔的字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="provider"></param>
        /// <returns></returns>
        public static String ListToString<T>(this List<T> list, string Spacer = ",", IFormatProvider provider = null)
        {
            return ListToString<T>(list.ToArray(), Spacer, provider);
        }

        /// <summary>
        /// 列表转换成逗号相隔的字符串
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public static String ListToString<T>(this T[] list, string Spacer = ",", IFormatProvider provider = null)
        {
            StringBuilder sb = new StringBuilder();
            if (list != null && list.Length > 0)
            {
                foreach (var str in list)
                {
                    if (str != null && !String.IsNullOrEmpty(Convert.ToString(str)))
                    {
                        if (String.IsNullOrEmpty(sb.ToString()))
                        {
                            sb.Append(str);
                        }
                        else
                        {
                            sb.AppendFormat(provider, "{0}{1}", Spacer, str);
                        }
                    }
                }
            }
            return sb.ToString();
        }
        #endregion


        #region IsBegin/FinishWith Char
        /// <summary>
        /// 判断字符串是否以指定字符开头
        /// </summary>
        /// <param name="s"></param>
        /// <param name="c"></param>
        /// <returns></returns>
        public static bool IsBeginWith(this string s, char c) =>
            s.IsNullOrEmpty() ? false : s[0] == c;
        /// <summary>
        /// 判断字符串是否以指定字符开头
        /// </summary>
        /// <param name="s"></param>
        /// <param name="chars"></param>
        /// <returns></returns>
        public static bool IsBeginWithAny(this string s, IEnumerable<char> chars) =>
            s.IsNullOrEmpty() ? false : chars.Contains(s[0]);
        /// <summary>
        /// 判断字符串是否以指定字符开头
        /// </summary>
        /// <param name="s"></param>
        /// <param name="chars"></param>
        /// <returns></returns>
        public static bool IsBeginWithAny(this string s, params char[] chars) =>
            s.IsBeginWithAny(chars.AsEnumerable());
        /// <summary>
        /// 判断字符串是否以指定字符开头
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="comparisonType"></param>
        /// <returns></returns>
        public static bool IsBeginWith(this string a, string b, StringComparison comparisonType = StringComparison.Ordinal) =>
            (a.IsNull() || b.IsNull()) ? false : a.StartsWith(b, comparisonType);
        /// <summary>
        /// 判断字符串是否以指定字符开头
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="ignoreCase"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public static bool IsBeginWith(this string a, string b, bool ignoreCase, CultureInfo culture) =>
            (a.IsNull() || b.IsNull()) ? false : a.StartsWith(b, ignoreCase, culture);
        /// <summary>
        /// 判断字符串是否以指定字符结尾
        /// </summary>
        /// <param name="s"></param>
        /// <param name="c"></param>
        /// <returns></returns>
        public static bool IsFinishWith(this string s, char c)
        {
            if (s.IsNullOrEmpty()) return false;
            return s.Last() == c;
        }
        /// <summary>
        /// 判断字符串是否以指定字符结尾
        /// </summary>
        /// <param name="s"></param>
        /// <param name="chars"></param>
        /// <returns></returns>
        public static bool IsFinishWithAny(this string s, IEnumerable<char> chars) =>
            s.IsNullOrEmpty() ? false : chars.Contains(s.Last());
        /// <summary>
        /// 判断字符串是否以指定字符结尾
        /// </summary>
        /// <param name="s"></param>
        /// <param name="chars"></param>
        /// <returns></returns>
        public static bool IsFinishWithAny(this string s, params char[] chars) =>
            s.IsFinishWithAny(chars.AsEnumerable());
        /// <summary>
        /// 判断字符串是否以指定字符结尾
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="comparisonType"></param>
        /// <returns></returns>
        public static bool IsFinishWith(this string a, string b, StringComparison comparisonType = StringComparison.Ordinal) =>
            (a.IsNull() || b.IsNull()) ? false : a.EndsWith(b, comparisonType);
        /// <summary>
        /// 判断字符串是否以指定字符结尾
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="ignoreCase"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public static bool IsFinishWith(this string a, string b, bool ignoreCase, CultureInfo culture) =>
            (a.IsNull() || b.IsNull()) ? false : a.EndsWith(b, ignoreCase, culture);
        #endregion

        #region IsDefault/IsNull/IsEmpty
        /// <summary>
        /// 判断字典键值类型是否未赋值
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="keyValuePair"></param>
        /// <returns></returns>
        public static bool IsDefault<TKey, TValue>(this KeyValuePair<TKey, TValue> keyValuePair) =>
            default(KeyValuePair<TKey, TValue>).Equals(keyValuePair);

        /// <summary>
        /// 判断是否为Null
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool IsNull(this object obj) => obj == null;
        /// <summary>
        /// 判断是否为非Null
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool IsNotNull(this object obj) => obj != null;
        /// <summary>
        /// 判断集合是否为空或Null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumerable"></param>
        /// <returns></returns>
        public static bool IsEmpty<T>(this IEnumerable<T> enumerable) =>
            enumerable.IsNull() || !enumerable.Any();
        /// <summary>
        /// 判断集合是否不为空或Null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumerable"></param>
        /// <returns></returns>
        public static bool IsNotEmpty<T>(this IEnumerable<T> enumerable) =>
            enumerable.IsNotNull() && enumerable.Any();



        /// <summary>
        /// 返回一个值，该值指示指定的子串是否出现在此字符串‘,中。
        /// </summary>
        /// <param name="str"></param>
        /// <param name="words"></param>
        /// <returns></returns>
        public static bool Contains(this string str, string word, StringComparison comparisonType = StringComparison.Ordinal)
        {
            return !str.IsNullOrWhiteSpace() && str.IndexOf(word, comparisonType) >= 0;
        }


        /// <summary>
        /// 返回一个值，该值指示指定的子串是否出现在此字符串‘,中。
        /// </summary>
        /// <param name="str"></param>
        /// <param name="words"></param>
        /// <returns></returns>
        public static bool Contains(this string str, params string[] words)
        {
            var contain = false;
            foreach (var word in words)
                if (contain = str.Contains(word))
                    break;
            return contain;
        }

        public static bool Contains(this string[] words1, params string[] words2)
        {
            var contain = false;

            var ListWords2 = words2.ToList<String>();
            if (words1 != null && words1.Length > 0)
            {
                foreach (var word in words1)
                {
                    if (contain = ListWords2.Contains(word))
                    {
                        contain = true;
                        break;
                    }

                }

            }

            return contain;
        }

        #endregion

        #region Is对比判断
        /// <summary>
        /// 判断字符串是否为空
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string str) => string.IsNullOrEmpty(str);
        /// <summary>
        /// 判断字符串是否为空/空格
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsNullOrWhiteSpace(this string str) => string.IsNullOrWhiteSpace(str);

        /// <summary>
        /// 对比两个字符串是否相等
        /// </summary>
        /// <param name="str"></param>
        /// <param name="value"></param>
        /// <param name="comparison"></param>
        /// <returns></returns>
        public static bool IsEqual(this string str, string value, StringComparison comparison = StringComparison.Ordinal) => (str.IsNull() || value.IsNull()) ? false : str.Equals(value, comparison);
        /// <summary>
        /// 对比字符串是否与列出的相等
        /// </summary>
        /// <param name="str"></param>
        /// <param name="values"></param>
        /// <param name="comparison"></param>
        /// <returns></returns>
        public static bool IsEqual(this string str, string[] values, StringComparison comparison = StringComparison.Ordinal)
        {
            foreach (var value in values)
                if (str.Equals(value, comparison))
                    return true;
            return false;
        }
        /// <summary>
        /// 对比两个字符串是否相等（忽略大小写）
        /// </summary>
        /// <param name="str"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsEqualIgnoreCase(this string str, string value) => str.IsEqual(value, StringComparison.OrdinalIgnoreCase);
        /// <summary>
        /// 对比字符串是否与列出的相等（忽略大小写）
        /// </summary>
        /// <param name="str"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static bool IsEqualIgnoreCase(this string str, string[] values) => str.IsEqual(values, StringComparison.OrdinalIgnoreCase);

        /// <summary>
        /// 判断字符串是否是邮箱地址
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsEmailAddress(this string str) =>
            str.IsNullOrEmpty() ? false : new Regex(@"[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?").IsMatch(str.Trim());
        /// <summary>
        /// 判断字符串是否是手机号码
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsMobilePhoneNumber(this string str) =>
            str.IsNullOrEmpty() ? false : new Regex(@"^1[0-9]{10}$").IsMatch(str.Trim());


        /// <summary>
        /// 判断字符是不是汉字
        /// </summary>
        /// <param name="text">待判断字符或字符串</param>
        /// <returns>true是 false不是</returns>
        public static bool IsChineseRegString(this string text) => Regex.IsMatch(text, @"[\u4e00-\u9fbb]+$");

        #endregion

        #region IDictionary
        /// <summary>
        /// 获取字典值
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dict"></param>
        /// <param name="key"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static TValue SelectValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default(TValue)) =>
             dict.ContainsKey(key) ? dict[key] : defaultValue;

        /// <summary>
        /// 追加合并字典
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dict"></param>
        /// <param name="dictionary"></param>
        /// <returns></returns>
        public static IDictionary<TKey, TValue> Union<TKey, TValue>(this IDictionary<TKey, TValue> dict, IDictionary<TKey, TValue> dictionary)
        {
            foreach (var item in dictionary)
                dict[item.Key] = item.Value;
            return dict;
        }

        /// <summary>
        /// 获取字典值
        /// </summary>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dict"></param>
        /// <param name="keys"></param>
        /// <returns></returns>
        public static IEnumerable<TValue> SelectKeyContainsValues<TValue>(this IDictionary<string, TValue> dict, params string[] keys) =>
            dict.Where(d => d.Key.Contains(keys)).Select(d => d.Value);

        /// <summary>
        /// 获取字典值
        /// </summary>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dict"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static IEnumerable<TValue> SelectKeyStartWithValues<TValue>(this IDictionary<string, TValue> dict, string key) =>
            dict.Where(d => d.Key.StartsWith(key)).Select(d => d.Value);

        /// <summary>
        /// 获取字典值
        /// </summary>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dict"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static IEnumerable<TValue> SelectKeyEndWithValues<TValue>(this IDictionary<string, TValue> dict, string key) =>
            dict.Where(d => d.Key.EndsWith(key)).Select(d => d.Value);

        /// <summary>
        /// 拼接HTML标签属性项目
        /// </summary>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dict"></param>
        /// <returns></returns>
        public static string ToHtmlAttributesString<TValue>(this IDictionary<string, TValue> dict) =>
            dict.Select(d => d.Value.IsNull() ? d.Key : d.Value.ToString().IsEmpty() ? null : $"{d.Key}=\"{d.Value}\"")
                .Where(d => d.IsNotNull())
            .ToList()
                .ListToString(" ");
        #endregion

        #region Url 处理

        /// <summary>
        /// HTML转义为数据库合法模式
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        public static string HtmlEncode(this string html) => WebUtility.HtmlEncode(html);
        /// <summary>
        /// 被转义HTML的字符串还原
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        public static string HtmlDecode(this string html) => WebUtility.HtmlDecode(html);
        /// <summary>
        /// 将URL转义为合法参数地址
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string UrlEncode(this string url) => WebUtility.UrlEncode(url);
        /// <summary>
        /// 被转义的URL字符串还原
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string UrlDecode(this string url) => WebUtility.UrlDecode(url);
        /// <summary>
        /// 将 URL 中的参数名称/值编码为合法的格式。
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string EncodeAsUrlData(this string str) => Uri.EscapeDataString(str);


        /// <summary>
        /// 组装QueryString
        /// 参数之间用&amp;连接，首位没有符号，如：a=1&amp;b=2&amp;c=3
        /// </summary>
        /// <param name="formData"></param>
        /// <returns></returns>
        public static string ToQueryString(this Dictionary<string, string> formData)
        {
            if (formData.IsNull() || formData.Count == 0)
                return "";

            StringBuilder sb = new StringBuilder();
            var i = 0;
            foreach (var kv in formData)
            {
                if (kv.Value != null)
                {
                    if (sb.Length > 0)
                    {
                        sb.Append("&");
                    }
                    sb.AppendFormat("{0}={1}", kv.Key, kv.Value.EncodeAsUrlData());
                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// QueryString拆解为字典
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public static Dictionary<string, string> QueryStringToDictionary(this string query)
        {
            var dic = new Dictionary<string, string>();
            query = query.HtmlDecode().SplitThenGetLast("?");
            foreach (var item in query.SplitBySign("&"))
            {
                var key = item.SplitThenGetFirst("=");
                var value = item.Substring(key.Length + 1).UrlDecode();
                dic.Add(key, value);
            }
            return dic;
        }

      




        /// <summary>
        /// 转换页面url
        /// </summary>
        /// <param name="url"></param>
        /// <param name="formData"></param>
        /// <returns></returns>
        public static String ToUrl(this String url, Dictionary<string, string> formData)
        {
            return $"{url}?{formData.ToQueryString()}";
        }



        #endregion


        #region Cookie 处理

        /// <summary>
        /// 处理Cookies
        /// </summary>
        /// <param name="Cookies">aa=11;bb=22;</param>
        /// <returns></returns>
        public static CookieCollection ToCookieCollection(this String Cookies)
        {
            var CookieColls = new CookieCollection();
            foreach (var cookie in Cookies.SplitBySignWithoutEmpty(";"))
            {
                if (cookie.Contains("="))
                {
                    CookieColls.Add(new Cookie(cookie.SplitThenGetFirst("="), cookie.SplitThenGetLast("=")));
                }
            }
            return CookieColls;
        }

        /// <summary>
        /// 处理Cookies
        /// </summary>
        /// <param name="Cookies"></param>
        /// <returns></returns>
        public static CookieContainer ToCookieContainer(this String Cookies)
        {
            var CookieColls = new CookieContainer();
            if(!string.IsNullOrWhiteSpace(Cookies) && Cookies.Contains("="))
            {
                CookieColls.Add(Cookies.ToCookieCollection());
            }
            return CookieColls;
        }

        /// <summary>
        /// 处理Cookies
        /// </summary>
        /// <param name="Cookies"></param>
        /// <returns></returns>
        public static string ToCookies(this CookieCollection Cookies)
        {
            var strCookie = new List<string>();
            foreach(Cookie cookie in Cookies)
            {
                strCookie.Add($"{cookie.Name}={cookie.Value}");
            }
            return strCookie.ListToString(";");
        }

            #endregion



        }
}
